/* //device/system/broncho-ril/reference-ril.c
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <telephony/ril.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <alloca.h>
#include "atchannel.h"
#include "at_tok.h"
#include "ril_util.h"
#include "ppp_ctrl.h"
#include <getopt.h>
#include "broncho-ril.h"
#include "cutils/properties.h"

#define LOG_TAG "RIL"
#include "broncho_log.h"

#define PATH_PPP_OPERSTATE 		"/sys/class/net/ppp0/operstate"
#define UNKNOWN_ASCII_STRING 	"unknown"
#define UP_ASCII_STRING			"up"
#define DOWN_ASCII_STRING		"down"
#define PPP_MAX_CONNECTING_POLL_TIMES   10  /*setup ppp connection*/
#define PPP_DAEMON_START_POLL_TIMES     4   /*start ppp daemon*/


typedef enum _DataCallState
{
	DATA_CALL_STATE_NULL,
	DATA_CALL_STATE_CONNECTING,
	DATA_CALL_STATE_CONNECTED,
	DATA_CALL_STATE_DISCONNECTING,
	DATA_CALL_STATE_NR
}DateCallState;

typedef struct _GprsContext
{
	DateCallState 	state;
	RIL_Token		disconnect_token;
	RIL_Token		orig_setup_token;
	RIL_Token		setup_token;
	int             poll_times;
	int             poll_ppp;

	PppCtrl*		ppp_ctrl;

	char			apn[32];
}GprsContext;

#define CID_DSDS ((getSimId() == SIM_ID_DUAL) ? 2 : 1)

static const struct timeval TIMEVAL_GPRSPOLL = {5,0};
static const struct timeval TIMEVAL_GPRSFIRSTPOLL = {5, 0};
static const struct timeval TIMEVAL_DISCONNECTPOLL = {2, 0};

static void responseDeactivateDataCall(RIL* thiz, RIL_Token t);
static void unsolDataCallListChanged(RIL* thiz);
static void gprs_on_ppp_state_changed(PppCtrl* ppp_ctrl, PppState state, RIL* thiz);
static void getDataCallResponse(RIL* thiz, RIL_Data_Call_Response_v6* resp);
static void freeRILDataCallResponseItems(RIL_Data_Call_Response_v6* resp);

static GprsContext* getGprsContext(RIL* thiz)
{
	GprsContext* ctx = RIL_GetPrivInfo(thiz, "gprs_ctx");
	
	if(ctx == NULL)
	{
		ctx = calloc(1, sizeof(GprsContext));
		ctx->ppp_ctrl = ppp_ctrl_create((OnPppStateChangedFunc)gprs_on_ppp_state_changed, thiz);
		ppp_ctrl_stop_pppd(ctx->ppp_ctrl);
		RIL_SetPrivInfo(thiz, ctx, "gprs_ctx");
	}

	return ctx;
}

static void handle_ppp_state_idle(RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);
	
	if(ctx->state == DATA_CALL_STATE_CONNECTING)
	{
		if(ctx->setup_token != NULL) 
		{
			//broncho modify by lyl
			//ril_command_exec_for_result(thiz, "AT+CGACT=0,2");
			ppp_ctrl_stop_pppd(ctx->ppp_ctrl);
			RIL_onRequestComplete(thiz, ctx->setup_token, RIL_E_GENERIC_FAILURE, NULL, 0);
		}
		ctx->setup_token = NULL;        
		ctx->state = DATA_CALL_STATE_NULL;
		unsolDataCallListChanged(thiz);
	} 
	else if(ctx->state == DATA_CALL_STATE_CONNECTED) 
	{
		ctx->state = DATA_CALL_STATE_NULL;
		unsolDataCallListChanged(thiz);
	} 
	else if(ctx->state == DATA_CALL_STATE_DISCONNECTING)
	{
		if(ctx->disconnect_token != NULL)
		{
			responseDeactivateDataCall(thiz, ctx->disconnect_token);
			ctx->disconnect_token = NULL;
		}
		if(ctx->orig_setup_token != NULL) 
		{
			RIL_onRequestComplete(thiz, ctx->orig_setup_token, RIL_E_GENERIC_FAILURE, NULL, 0);
			ctx->orig_setup_token = NULL;
		}
		ctx->state = DATA_CALL_STATE_NULL;
	}
	return;
}

static void handle_ppp_state_up(RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);

	if(ctx->state == DATA_CALL_STATE_CONNECTING && ctx->setup_token != NULL)
	{
		RIL_Data_Call_Response_v6 response;
		memset(&response, 0 , sizeof(RIL_Data_Call_Response_v6));
		getDataCallResponse(thiz, &response);
		ALOGD("handle_ppp_state_up  dev: %s\n", response.ifname);
		RIL_onRequestComplete(thiz, ctx->setup_token, RIL_E_SUCCESS, &response, sizeof(RIL_Data_Call_Response_v6));
		ctx->setup_token = NULL;
		ctx->state = DATA_CALL_STATE_CONNECTED;
		freeRILDataCallResponseItems(&response);
	}

	return;
}

static void handle_ppp_state_down(RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);
	if(ctx->state == DATA_CALL_STATE_CONNECTED)
	{
		ctx->state = DATA_CALL_STATE_NULL;
		ppp_ctrl_stop_pppd(ctx->ppp_ctrl);
		unsolDataCallListChanged(thiz);
	}

	return;
}

static void gprs_on_ppp_state_changed(PppCtrl* ppp_ctrl, PppState state, RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);

	ALOGD("%s: state=%d, data_state=%d, setup_token=%p", __func__,
				state, ctx->state, ctx->setup_token);
	switch(state)
	{
	case PPP_STATE_IDLE:
		handle_ppp_state_idle(thiz);
		break;

	case PPP_STATE_UP:
		handle_ppp_state_up(thiz);
		break;

	case PPP_STATE_DOWN:
		handle_ppp_state_down(thiz);
		break;

	default:
		ALOGD("%s: error state", __func__);
		break;
	}
}

static void startConnectPpp(RIL* thiz, const char* user, const char* pwd, int authtype, RIL_Token t)
{
	GprsContext* ctx = getGprsContext(thiz);

	if(ctx->state == DATA_CALL_STATE_DISCONNECTING && ctx->disconnect_token != NULL)
	{
		responseDeactivateDataCall(thiz, ctx->disconnect_token);
		ctx->disconnect_token = NULL;
	}

	/*start pppd_gprs service*/
	ctx->state = DATA_CALL_STATE_CONNECTING;
	ctx->setup_token = t;
	char name[255] = {0};
    RIL_GetDataChannelDeviceName(thiz, name);
	ppp_ctrl_start_pppd(ctx->ppp_ctrl,
			name, user, pwd, authtype);
	return;
}

static void responseDeactivateDataCall(RIL* thiz, RIL_Token t)
{
	ALOGD("responseDeactivateDataCall: \n");
	
	if(!RIL_ExistCalls(thiz))
	{
		char cmd[32] = {0};
		snprintf(cmd, 32, "AT+CGACT=0,%d", CID_DSDS);
		at_command_exec_for_result(RIL_GetAtChannel(thiz), cmd, 40000);  //broncho modify by lyl
	}

	if(t != NULL)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
}

static void disconnectPpp(RIL* thiz, RIL_Token t)
{
	GprsContext* ctx = getGprsContext(thiz);
	
	if(ctx->state != DATA_CALL_STATE_NULL)
	{
		/*stop pppd_gprs service*/
		if(ctx->state == DATA_CALL_STATE_DISCONNECTING && ctx->disconnect_token != NULL)
		{
			ALOGD("respDisconnect: t=%p", ctx->disconnect_token);
			RIL_onRequestComplete(thiz, ctx->disconnect_token, RIL_E_GENERIC_FAILURE, NULL, 0);
			ctx->disconnect_token = NULL;
		}
		ctx->state = DATA_CALL_STATE_DISCONNECTING;
		ctx->disconnect_token= t;
		if(ctx->setup_token != NULL && t == NULL) 
		{
			//this called from requestSetupDataCall
			ALOGE("Opps! last setup token has not null. %p", ctx->setup_token);
			ctx->orig_setup_token = ctx->setup_token;
			ctx->setup_token = NULL;
		}
		ALOGD("%s", __func__);
		ppp_ctrl_stop_pppd(ctx->ppp_ctrl);
	}
	else
	{
		responseDeactivateDataCall(thiz, t);
	}

	return;
}

//broncho start add by longyanliang
static int checkCGACT(RIL* thiz)
{
	ATResponse * pp_resp = NULL;
	ATLine *p_cur = NULL;
	int state = -1;
	int number = 0;

	ril_send_command_multiline(thiz, "AT+CGACT?", "+CGACT:", &pp_resp);
	for(p_cur = pp_resp->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next)
	{
        ALOGD("checkCGACT: now parse%s", p_cur->line);
		at_line_scanf_int_int(p_cur->line, &number, &state);
        if(state == 1)
		{
			break;
		}
	}

	ALOGD("checkCGACT: number=%d , state=%d" , number, state);
	return state;	
}
//broncho start add by longyanliang

static void requestSetupDataCall(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	int			success = 0;
	ATChannel* 	data_channel = NULL;
	char		cmd[128] = {0};
	char*		apn = NULL;
	char *response[2] = { "1", "/dev/data0"};
	GprsContext* ctx = getGprsContext(thiz);
	int authtype = 0;
	ATResponse * pp_resp = NULL;
	char * result = NULL;

	if(ctx->state != DATA_CALL_STATE_NULL)
	{
		disconnectPpp(thiz, NULL);
		sleep(10);
	}

	ALOGD("requestSetupDataCall: apn=%s, user=%s, pwd=%s\n", 
			((char**)data)[2], ((char**)data)[3], ((char**)data)[4]);
	authtype = atoi(((char**)data)[5]);
	RIL_OpenDataChannel(thiz);

	ril_send_command(thiz, "AT+CGATT=1", NULL);	
	ril_send_command(thiz, "AT+CGATT?", NULL);	
	ril_send_command(thiz, "AT+CGACT?", NULL);
	
	//broncho start add by lyl
	int pdpState = checkCGACT(thiz);
    if( pdpState == 1 )
	{
		//snprintf(cmd, 128, "AT+CGACT=0,%d", CID_DSDS);
	    if(!at_command_exec_for_result(RIL_GetAtChannel(thiz), "AT+CGACT=0,1", 40000))
	    {
		   at_command_exec_for_result(RIL_GetAtChannel(thiz), "AT+CGACT=0,1", 40000);
	    }
        if(!at_command_exec_for_result(RIL_GetAtChannel(thiz), "AT+CGACT=0,2", 40000))
	    {
		   at_command_exec_for_result(RIL_GetAtChannel(thiz), "AT+CGACT=0,2", 40000);
	    }
	}
	//broncho end add by lyl

	do
	{
		if ((data_channel = RIL_GetDataChannel(thiz)) == NULL)
		{
			break;
		}

		apn = ((char**)data)[2];
		sleep(2); //NOTICE-BUG this is neccery for A13! by yinlijun
		snprintf(cmd, 128, "AT+CGDCONT=%d,\"IP\",\"%s\",\"\",0,0", CID_DSDS, apn);
		snprintf(ctx->apn, 32, "%s", apn);
		if(!at_command_exec_for_result(RIL_GetAtChannel(thiz), cmd, DEFAULT_AT_TIMEOUT_MSEC))
		{
			break;
		}
		
        snprintf(cmd, 128, "AT+CGPCO=0,\"\",\"\",%d", CID_DSDS);
		if(!at_command_exec_for_result(RIL_GetAtChannel(thiz), cmd, DEFAULT_AT_TIMEOUT_MSEC))
		{
			break;
		}
		
        snprintf(cmd, 128, "AT+CGEQREQ=%d,2,0,0,0,0,2,0,\"1E4\",\"0E0\",3,0,0", CID_DSDS);
		if(!at_command_exec_for_result(RIL_GetAtChannel(thiz), cmd, DEFAULT_AT_TIMEOUT_MSEC))
		{
			break;
		}
	
        snprintf(cmd, 128, "AT+CGDATA=\"PPP\",%d", CID_DSDS);
		if(!at_command_exec_for_result(RIL_GetAtChannel(thiz), cmd, 10000/*DEFAULT_AT_TIMEOUT_MSEC*/))
		{
			break;
		}
		
        snprintf(cmd, 128, "AT+SIPCONFIG=%d", CID_DSDS);
		ril_send_command(thiz, cmd, NULL);
		success = 1;
	}while(0);
		RIL_CloseDataChannel(thiz);

	if(success)
	{
		startConnectPpp(thiz, ((char**)data)[3], ((char**)data)[4], authtype, t);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}

	return;

}

static void requestDeactivateDataCall(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	disconnectPpp(thiz, t);
	char		cmd[128] = {0};
    snprintf(cmd, 128, "AT+CGACT=0,%d", CID_DSDS);
	if(!at_command_exec_for_result(RIL_GetAtChannel(thiz), cmd, 40000))
	{
		at_command_exec_for_result(RIL_GetAtChannel(thiz), cmd, 40000);
	}
	at_command_exec_for_result(RIL_GetAtChannel(thiz), "AT+SGFD", 40000);
	return;
}

static void requestSetDataAttach(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	ril_send_command(thiz, "AT+CGATT=1", NULL);	
    RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
}

static int convertRegistrationState(int state)
{
	if(state == 1)
	{
		return 1; //registed
	}
	else
	{
		return 0;//not registed.
	}
}

//convert to telephony format.
static int convertNetworkType(int type)
{
	switch(type)
	{
		case 0: //GSM
			return 1;
		case 1: //GSM
			return 1;
		case 2: //UTRAN
			return 1;
		case 3: //EGPRS
			return 1;
		case 4: //HSDPA
			return 9;
		case 5: //HSUPA
			return 10;
		case 6: //HSDPA + HSUPA
			return 10;
		default:
			return 0;//unknown
	}
}

static void requestGprsRegistrationState(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	int err = 0;
	ATResponse* p_response = NULL;
	char* responseStr[4] = {0};
	int state = 0;
	int network_type = 0;
	int skip = 0;
	char* skip_str = NULL;
	char* line = NULL;
	err = ril_send_command_singleline(thiz, "AT+CGREG?", "+CGREG:", &p_response);
	//err = ril_send_command_singleline(thiz, "AT+CREG?", "+CREG:", &p_response);
	if (err != 0 || p_response == NULL || p_response->p_intermediates == NULL || p_response->p_intermediates->line == NULL) goto error;

	line = p_response->p_intermediates->line;
	err = at_tok_nextint(&line, &skip);
	if (err < 0) goto error;
	err = at_tok_nextint(&line, &state);
	if (err < 0) goto error;
	/*
	err = at_tok_nextstr(&line, &skip_str);
	if (err < 0) goto error;
	err = at_tok_nextstr(&line, &skip_str);
	if (err < 0) goto error;
    */   
	network_type = 1;
	asprintf(&responseStr[0], "%d", convertRegistrationState(state));
	asprintf(&responseStr[3], "%d", convertNetworkType(network_type));

	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, responseStr, sizeof(responseStr));
	free(responseStr[0]);
	free(responseStr[3]);

	at_response_free(p_response);
	return;

error:
	RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	at_response_free(p_response);
}


/*static void requestGprsRegistrationState(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	int err = 0;
	ATResponse* p_resp = NULL;
	char* responseStr[4];
	int state = 0;

	if(ril_command_exec_singleline_for_result(thiz, "AT+CGREG?", "+CGREG", &p_resp))
	{
		int skip = 0;
		char* line = NULL;

		line = p_resp->p_intermediates->line;
		do
		{
			if((err = at_tok_start(&line)) < 0)
			{
				break;
			}

			if ((err = at_tok_nextint(&line, &skip)) < 0)
			{
				break;
			}
            
			err = at_tok_nextint(&line, &state);
		}while(0);
	}

	if(err < 0)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}
	else
	{
		if(state == 8) state = 0;// patch for spreadtrum registration state 8, remember sync change ril-mm.c#requestRegistrationState
		asprintf(&responseStr[0], "%d", state);
		
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, responseStr, sizeof(char*));
		free(responseStr[0]);
	}

	at_response_free(p_resp);

	return;
}*/

static void requestLastDataCallFailCause(RIL* thiz, void* data, int datalen, RIL_Token t)
{
	int failCase = CALL_FAIL_NORMAL;

	ALOGD("requestLastDataCallFailCause \n");
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, &failCase, sizeof(failCase));

	return;
}

static void getDataCallResponse(RIL* thiz, RIL_Data_Call_Response_v6* resp)
{
	GprsContext* ctx = getGprsContext(thiz);
	char addr[PROPERTY_VALUE_MAX] = {0};

	property_get("net.ppp0.local-ip", addr, "");
	ALOGD("getDataCallResponse  addr %s\n", addr);
	resp->cid = CID_DSDS;
	resp->type = "IP";
	resp->addresses = strdup(addr);
	if(ctx->state == DATA_CALL_STATE_NULL)
	{
		resp->active = 0;
	}
	else
	{
		resp->active = 2;
	}

	resp->status = 0;
	
	char* name = ppp_ctrl_get_dev_name(ctx->ppp_ctrl);
	if(name != NULL) 
	{
		resp->ifname = strdup(name);
	}

	return;
}

static void freeRILDataCallResponseItems(RIL_Data_Call_Response_v6* resp)
{
	if(resp != NULL) 
	{
		if(resp->ifname != NULL)
		{
			free(resp->ifname);
			resp->ifname = NULL;
		}
		if(resp->addresses != NULL)
		{
			free(resp->addresses);
			resp->addresses = NULL;
		}
	}
}

static void unsolDataCallListChanged(RIL* thiz)
{
	RIL_Data_Call_Response_v6 responses;

	memset(&responses, 0 , sizeof(RIL_Data_Call_Response_v6));
	getDataCallResponse(thiz, &responses);
	RIL_onUnsolicitedResponse(thiz, RIL_UNSOL_DATA_CALL_LIST_CHANGED, 
		&responses, sizeof(RIL_Data_Call_Response_v6));
	
	freeRILDataCallResponseItems(&responses);
	return;
}

static void requestDataCallList(RIL* thiz, void* data, int datalen, RIL_Token t)
{
	RIL_Data_Call_Response_v6 responses;
	
	memset(&responses, 0 , sizeof(RIL_Data_Call_Response_v6));
	getDataCallResponse(thiz, &responses);
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, &responses,sizeof(RIL_Data_Call_Response_v6));

	return;
}


int RIL_initialize_gprs (RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);
	at_command_exec_for_result(RIL_GetAtChannel(thiz), "ATE0", DEFAULT_AT_TIMEOUT_MSEC);
	return 1;
}


int RIL_onUnsolicited_gprs (RIL* thiz, const char* s)
{
	int ret = 0;
	if(strStartsWith(s, "NO_CARRIER"))
	{
		ALOGD("into RIL_onUnsolicited_gprs: NO_CARRIRE");
		ret = 1;
	}

	return ret;
}


int RIL_onRequest_gprs (RIL* thiz, int request, void *data, size_t datalen, RIL_Token t)
{
	switch (request)
	{
	case RIL_REQUEST_SETUP_DATA_CALL:
		requestSetupDataCall(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_DEACTIVATE_DATA_CALL:
		requestDeactivateDataCall(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE:
		requestLastDataCallFailCause(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_DATA_CALL_LIST:
		requestDataCallList(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_GPRS_REGISTRATION_STATE:
		requestGprsRegistrationState(thiz, data, datalen, t);
		break;

    case RIL_REQUEST_SET_DATA_ATTACH:
        requestSetDataAttach(thiz, data, datalen, t);
        break;

	default:
		RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
		break;

	}

	return 1;
}

